﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using CSharpLexer;

namespace CSharpLexer_Test {
	[TestClass]
	public class CSharpLexerTests {
		private TestContext testContextInstance;

		/// <summary>
		///Gets or sets the test context which provides
		///information about and functionality for the current test run.
		///</summary>
		public TestContext TestContext {
			get {
				return testContextInstance;
			}
			set {
				testContextInstance = value;
			}
		}

		#region Additional test attributes
		//
		// You can use the following additional attributes as you write your tests:
		//
		// Use ClassInitialize to run code before running the first test in the class
		// [ClassInitialize()]
		// public static void MyClassInitialize(TestContext testContext) { }
		//
		// Use ClassCleanup to run code after all tests in a class have run
		// [ClassCleanup()]
		// public static void MyClassCleanup() { }
		//
		// Use TestInitialize to run code before running each test 
		// [TestInitialize()]
		// public void MyTestInitialize() { }
		//
		// Use TestCleanup to run code after each test has run
		// [TestCleanup()]
		// public void MyTestCleanup() { }
		//
		#endregion

		[TestInitialize()]
		public void MyTestInitialize() {
			l = new CSharpLexer.CSharpLexer();
		}

		CSharpLexer.CSharpLexer l;
		string s;

		[TestMethod]
		public void SingleIdentifier() {
			s = "test";
			AssertTokenStream(new Token[] {
				new Token(Token.TokenType.IDENTIFIER, "test", 0, 4),
				new Token(Token.TokenType.EOF, null, 4, 0),
			}.AsEnumerable());
		}

		[TestMethod]
		public void TwoIdentifiersSpace() {
			s = "hello world";
			AssertTokenStream(new Token[] {
				new Token(Token.TokenType.IDENTIFIER, "hello", 0, 5),
				new Token(Token.TokenType.IDENTIFIER, "world", 6, 5),
				new Token(Token.TokenType.EOF, null, 11, 0),
			}.AsEnumerable());
		}

		[TestMethod]
		public void TwoIdentifiersNewline() {
			s = "hello\nworld";
			AssertTokenStream(new Token[] {
				new Token(CSharpLexer.Token.TokenType.IDENTIFIER, "hello", 0, 5),
				new Token(CSharpLexer.Token.TokenType.IDENTIFIER, "world", 6, 5),
				new Token(Token.TokenType.EOF, null, 11, 0),
			}.AsEnumerable());
		}

		[TestMethod]
		public void EmptyClass() {
			s = "class Test {}";
			AssertTokenStream(new Token[] {
				new Token(CSharpLexer.Token.TokenType.KEYWORD, "class", 0, 5),
				new Token(CSharpLexer.Token.TokenType.IDENTIFIER, "Test", 6, 4),
				new Token(CSharpLexer.Token.TokenType.OPERATOR_OR_PUNCTUATOR, "{}", 11, 2),
				new Token(Token.TokenType.EOF, null, 13, 0),
			}.AsEnumerable());
		}

		[TestMethod]
		public void HelloWorld() {
		    s = "class Test {\n" +
		        "    static void Main(string[] args) {\n" +
		        "        System.Console.WriteLine(\"Hello world!\");\n" +
		        "    }\n" +
		        "}\n";
			AssertTokenStream(new Token[] {
				new Token(CSharpLexer.Token.TokenType.KEYWORD, "class", 0, 5),
				new Token(CSharpLexer.Token.TokenType.IDENTIFIER, "Test", 6, 4),
				new Token(CSharpLexer.Token.TokenType.OPERATOR_OR_PUNCTUATOR, "{", 11, 1),
				new Token(CSharpLexer.Token.TokenType.KEYWORD, "static", 17, 6),
				new Token(CSharpLexer.Token.TokenType.KEYWORD, "void", 24, 4),
				new Token(CSharpLexer.Token.TokenType.IDENTIFIER, "Main", 29, 4),
				new Token(CSharpLexer.Token.TokenType.OPERATOR_OR_PUNCTUATOR, "(", 33, 1),
				new Token(CSharpLexer.Token.TokenType.KEYWORD, "string", 34, 6),
				new Token(CSharpLexer.Token.TokenType.OPERATOR_OR_PUNCTUATOR, "[]", 40, 2),
				new Token(CSharpLexer.Token.TokenType.IDENTIFIER, "args", 43, 4),
				new Token(CSharpLexer.Token.TokenType.OPERATOR_OR_PUNCTUATOR, ")", 47, 1),
				new Token(CSharpLexer.Token.TokenType.OPERATOR_OR_PUNCTUATOR, "{", 49, 1),
				new Token(CSharpLexer.Token.TokenType.IDENTIFIER, "System", 59, 6),
				new Token(CSharpLexer.Token.TokenType.OPERATOR_OR_PUNCTUATOR, ".", 65, 1),
				new Token(CSharpLexer.Token.TokenType.IDENTIFIER, "Console", 66, 7),
				new Token(CSharpLexer.Token.TokenType.OPERATOR_OR_PUNCTUATOR, ".", 73, 1),
				new Token(CSharpLexer.Token.TokenType.IDENTIFIER, "WriteLine", 74, 9),
				new Token(CSharpLexer.Token.TokenType.OPERATOR_OR_PUNCTUATOR, "(", 83, 1),
				new Token(CSharpLexer.Token.TokenType.STRING_LITERAL, "\"Hello world!\"", 84, 14),
				new Token(CSharpLexer.Token.TokenType.OPERATOR_OR_PUNCTUATOR, ");", 98, 2),
				new Token(CSharpLexer.Token.TokenType.OPERATOR_OR_PUNCTUATOR, "}", 105, 1),
				new Token(CSharpLexer.Token.TokenType.OPERATOR_OR_PUNCTUATOR, "}", 107, 1),
				new Token(Token.TokenType.EOF, null, 109, 0),
			}.AsEnumerable());
		}

		private void AssertTokenStream(IEnumerable<Token> expected) {
			IEnumerator<Token> expectedEnumerator = expected.GetEnumerator();
			StringBuilder result = new StringBuilder();
			foreach (Token t in l.Scan(s)) {
				result.Append(t.ToString() + " ");
				expectedEnumerator.MoveNext();
				Assert.AreEqual(expectedEnumerator.Current, t);
			}
			Assert.IsFalse(expectedEnumerator.MoveNext(), "Result was shorter than expected. It was: " + result);
		}
	}
}
